package core.net;

import core.buffer.Buffer;
import core.buffer.BufferManager;
import net.PoolExecutor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

public class TCPServer {

    public final static int READY = 0;
    public final static int OPEN = 1;
    public final static int CLOSE = 2;
    private ServerSocket _socket;
    private int _status;
    private List<Connection> _connections;
    private BufferManager _bufferManager;
    private InetSocketAddress _address;
    private NetConfig _config;

    private Consumer<Connection> _connectedHandler;
    public void connectedHandler(Consumer<Connection> handler){
        _connectedHandler = handler;
    }


    public TCPServer(int port, NetConfig config) {
        _address = new InetSocketAddress(port);
        _status = READY;
        _connections = new ArrayList<>();
        _bufferManager = new BufferManager();
        _config = config;
        _connectedHandler = this::connected;
    }

    public void open() {
        try {
            _socket = new ServerSocket();
            _socket.bind(_address);
            _status = OPEN;
            startAccept();
            System.out.println("[Server] start with " + _address);
        } catch (IOException e) {
            close();
        }
    }

    private void startAccept() {
        if (_status == CLOSE) return;
        CompletableFuture.runAsync(this::accept, PoolExecutor.executor()).thenRunAsync(this::startAccept);
    }

    private void accept() {
        try {
            Socket socket = _socket.accept();
            socket.setSoTimeout(_config.ReceiveTimeout());
            Connection connection = new Connection(socket,_bufferManager,_config);
            _connectedHandler.accept(connection);
        } catch (IOException e) {
            close();
        }
    }

    private void connected(Connection connection)
    {
        Buffer buffer = new Buffer(4);
        buffer.writeInt(123);
        connection.write(buffer);
        connection.disconnectHandler(this::disconnected);
        _connections.add(connection);
    }

    private void disconnected(Connection connection)
    {
        _connections.remove(connection);
    }

    public void close() {
        try {
            _socket.close();
            for (Connection connection : _connections) {
                connection.close();
            }
            _connections.clear();
        } catch (Exception e) {

        }
        _status = CLOSE;
    }


}
